package com.github.niefy.common.exception;

import com.aliyun.oss.ServiceException;
import com.github.niefy.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author : [86188]
 * @description : pojo参数校验异常处理,各类请求异常需与RRExceptionHandler组合处理，保底异常处理
 * @createTime : [2023/2/20 9:24]
 * @updateUser : [86188]
 * @updateTime : [2023/2/20 9:24]
 * @ ControllerAdvice 是一个增强的 Controller。使用这个 Controller，可以实现三个方面的功能：
 *全局异常处理
 *全局数据绑定
 *全局数据预处理
 *只拦截Controller，不会拦截Interceptor的异常
 */
@Slf4j
@RestControllerAdvice(annotations = {RestController.class},
        basePackages = {"com.github.niefy.modules.third",
                "com.github.niefy.modules.wx"})
public class ValidateExceptionHandler {

    //处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
    @ResponseStatus(HttpStatus.BAD_REQUEST) //设置状态码为 400
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public R paramExceptionHandler(MethodArgumentNotValidException e) {
        BindingResult exceptions = e.getBindingResult();
        // 判断异常中是否有错误信息，如果存在就使用异常中的消息，否则使用默认消息
        if (exceptions.hasErrors()) {
            List<ObjectError> errors = exceptions.getAllErrors();
            if (!errors.isEmpty()) {
                // 这里列出了全部错误参数，按正常逻辑，只需要第一条错误即可
                FieldError fieldError = (FieldError) errors.get(0);
                return ErrorMessage.messageToR("A0154", fieldError.getDefaultMessage());
            }
        }
        return ErrorMessage.exceptionMsgToR("A0154", e);
    }

    //处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
    @ResponseStatus(HttpStatus.BAD_REQUEST) //设置状态码为 400
    @ExceptionHandler(BindException.class)
    public R BindExceptionHandler(@NotNull BindException e) {
        String errorMsg = e.getBindingResult().getAllErrors().stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining());
        return ErrorMessage.exceptionMsgToR("A0154", e, errorMsg);
    }

    //处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
    @ResponseStatus(HttpStatus.BAD_REQUEST) //设置状态码为 400
    @ExceptionHandler(ConstraintViolationException.class)
    public R ConstraintViolationExceptionHandler(@NotNull ConstraintViolationException e) {
        String errorMsg = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining());
        return ErrorMessage.exceptionMsgToR("A0154", e, errorMsg);
    }


    // 要求Content-type为application/json，但是内容类型却是text/plain或者text时会被该异常捕获
    @ResponseStatus(HttpStatus.BAD_REQUEST) //设置状态码为 400
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public R HttpMediaTypeNotSupportedExceptionHandler(@NotNull HttpMediaTypeNotSupportedException e) {
        String errorMsg = "本接口不接收application/json以外格式的数据，请检查入参是否是标准的json数据！\n" + e.getMessage();
        return ErrorMessage.exceptionMsgToR("A0155", e, errorMsg);
    }

    // 异常保底处理
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public R handleException(@NotNull Exception e) {
        String errorMsg = "系统异常，请联系开发人员进行排错！\n";
        return ErrorMessage.exceptionMsgToR("A0156", e, errorMsg);
    }

    // 运行异常保底处理
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(RuntimeException.class)
    public R handleRuntimeException(@NotNull Exception e) {
        return ErrorMessage.exceptionMsgToR("A0156", e, e.getMessage());
    }

    // 服务异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ServiceException.class)
    public R handleServiceException(@NotNull Exception e) {
        return ErrorMessage.exceptionMsgToR("A0156", e, e.getMessage());
    }

    // 请求方式异常（仅支持post请求）
    @ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R handleHttpRequestMethodNotSupportedException(@NotNull Exception e) {
        return ErrorMessage.exceptionMsgToR("A0156", e, e.getMessage());
    }

    // 前端传入的参数与后端controller接收的参数类型不匹配，json反序列化失败
    // 注意：当请求数据格式为非text或text/plain时，也会抛出该异常（application/javascript,application/xml,text/xml,text/html）
    // 无请求入参时也会抛出该异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public R handleHttpMessageNotReadableException(@NotNull Exception e) {
        return ErrorMessage.exceptionMsgToR("A0156", e, e.getMessage());
    }

    // 集合校验
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(NotReadablePropertyException.class)
    public R handleNotReadablePropertyException(@NotNull NotReadablePropertyException e) {
        return ErrorMessage.exceptionMsgToR("A0154", e, e.getMessage());
    }
}
